﻿using NPOI.SS.UserModel;
using NPOI.Util.Collections;
using Org.BouncyCastle.Utilities.Collections;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Single.Npoi
{
    public class NpoiEntity<T> where T : class, new()
    {
        private readonly List<T> list;

        public NpoiEntity(List<T> list)
        {
            this.list = list;
        }

        public DataTable ToDataTable(params string[] ignore)
        {
            var dt = new DataTable();

            var type = typeof(T);
            var properties = type.GetProperties();

            foreach (var prop in properties)
            {
                if (!ignore.Contains(prop.Name))
                {
                    dt.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
                }
            }

            foreach (T item in list)
            {
                var row = dt.NewRow();
                foreach (var prop in properties)
                {
                    if (!ignore.Contains(prop.Name))
                    {
                        row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                    }
                }
                dt.Rows.Add(row);
            }

            return dt;
        }

        public DataTable ToDataTable<T2>(NpoiMap<T2> maps)
        {
            var dt = new DataTable();

            Type type = typeof(T2);

            foreach (var map in maps.Values)
            {
                dt.Columns.Add(map.Key, Nullable.GetUnderlyingType(map.Type) ?? map.Type);
            }

            foreach (T item in list)
            {
                var row = dt.NewRow();

                foreach (var map in maps.Values)
                {
                    var temp = map.MemberExpr?.ToString();

                    if (!string.IsNullOrEmpty(temp))
                    {
                        var tempList = temp.Split('.').ToList();

                        if (tempList.Count == 2)
                        {
                            var prop = map.Property;

                            if (prop != null)
                            {
                                row[map.Key] = prop.GetValue(item) ?? DBNull.Value;
                            }
                        }
                        else//支持导航属性值导出
                        {
                            tempList.RemoveAt(0);

                            row[map.Key] = item.GetNestedPropertyValue(string.Join(".", tempList)) ?? DBNull.Value;
                        }
                    }
                }
                dt.Rows.Add(row);
            }

            return dt;
        }

        public List<int> ToWidthList<T2>(NpoiMap<T2> maps)
        {
            var list = new List<int>();

            foreach (var map in maps.Values)
            {
                if (map.Width != 0)
                {
                    list.Add(map.Width);
                }
            }

            return list;
        }

        public void ToExcel(string filename, params string[] ignore)
        {
            NpoiHelper.DataTableToExcel(ToDataTable(ignore), null, "sheet1", filename);
        }

        public void ToExcel<T2>(string filename, NpoiMap<T2> maps, bool isMerged = false)
        {
            NpoiHelper.DataTableToExcel(ToDataTable(maps), ToWidthList(maps), "sheet1", filename, isMerged);
        }

        public void ToCsv<T2>(string filename, NpoiMap<T2> maps)
        {
            NpoiHelper.DataTableToCsv(ToDataTable(maps), filename);
        }
    }

    public static class ReflectionHelper
    {
        /// <summary>
        /// 获取嵌套属性值（支持对象.属性.子属性格式）
        /// </summary>
        /// <param name="source">源对象</param>
        /// <param name="propertyPath">属性路径（如 "Customer.Address.City"）</param>
        /// <param name="throwOnNotFound">当属性不存在时是否抛出异常</param>
        /// <returns>属性值或 null</returns>
        public static object GetNestedPropertyValue(this object source, string propertyPath, bool throwOnNotFound = false)
        {
            if (source == null) return null;

            var currentObject = source;

            foreach (var propertyName in propertyPath.Split('.'))
            {
                var type = currentObject.GetType();

                var property = type.GetProperty(propertyName);

                if (property == null)
                {
                    if (throwOnNotFound)
                    {
                        throw new ArgumentException($"Property '{propertyName}' not found in type '{type.Name}'");
                    }

                    return null;
                }

                currentObject = property.GetValue(currentObject);

                if (currentObject == null) break; // 提前终止如果中间对象为null
            }

            return currentObject;
        }
    }
}